﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace fhe
{
  namespace math
  {
    public static class PolygonUtils
    {
      public class Pair<T>
      {
        public T A { get; set; }
        public T B { get; set; }

        public override string ToString()
        {
          return string.Format("{0} {1}", A, B);
        }
      }

      public static List<Pair<T>> Wrap<T>(this List<T> l)
      {
        return Enumerable.Range(0, l.Count)
          .Select(i => new Pair<T>()
          {
            A = l[i],
            B = l[(i + 1) % l.Count],
          }).ToList();
      }
    }

    public class Polygon : IShape
    {
      public List<int> Indices { get; set; }

      public Func<int, Vec2> GetVertex { get; set; }

      public List<Vec2> Vertices
      {
        get
        {
          if (vertices != null)
          {
            return vertices;
          }
          else if (Indices != null && GetVertex != null)
          {
            return Indices.Select(GetVertex).ToList();
          }
          else
          {
            throw new Exception();
          }
        }
        set { vertices = value; }
      }

      private List<Vec2> vertices;

      public bool Inside(Vec2 v)
      {
        return Vertices
          .Wrap()
          .Select(edge => Vec2.DistToLine(edge.A, edge.B, v))
          .ToList()
          .Wrap()
          .All(dists => dists.A * dists.B > 0);
      }

      public Vec2 SurfaceOffset(Vec2 v)
      {
        return Vertices
          .Wrap()
          .Select(edge => (edge.B - edge.A).Normalize().Perp() * Vec2.DistToLine(edge.A, edge.B, v))
          .OrderBy(offset => offset.Length())
          .First();
      }

      private class OffsetPair
      {
        public Vec2 Offset { get; set; }

        public List<int> Indices { get; set; }
      }

      public void SurfaceOffset(Vec2 v, Vec2? dir, out Vec2 offset, out List<int> indices)
      {
        List<OffsetPair> offsets = new List<OffsetPair>();
        List<Vec2> vertices = Vertices;
        foreach (var edge in Enumerable.Range(0, vertices.Count).ToList().Wrap())
        {
          Vec2 a = vertices[edge.A];
          Vec2 b = vertices[edge.B];
          double d = Vec2.DistToLine(a, b, v);
          Vec2 o = (b - a).Normalize().Perp() * d;
          if (d > 0 && (!dir.HasValue || dir.Value.Dot(o) > 0))
          {
            offsets.Add(new OffsetPair()
            {
              Offset = o,
              Indices = new List<int>() { edge.A, edge.B },
            });
          }
        }
        OffsetPair best = offsets.OrderBy(o => o.Offset.SquaredLength()).First();
        offset = best.Offset;
        indices = best.Indices;
      }
    }
  }
}
